From 3af5c83cd3f236761ea84b3618057a2aec87f0a2 Mon Sep 17 00:00:00 2001 From: Cosimo Cecchi Date: Fri, 18 Dec 2015 14:45:15 -0800 Subject: [PATCH] checkmenuitem: use a gadget for the check indicator This will make margins and other CSS properties work correctly on the indicator. --- gtk/gtkcheckmenuitem.c | 200 +++++++++++++++-------- gtk/gtkmenuitem.c | 6 + gtk/gtkmenuitemprivate.h | 1 + gtk/theme/Adwaita/_common.scss | 2 + gtk/theme/Adwaita/gtk-contained-dark.css | 8 + gtk/theme/Adwaita/gtk-contained.css | 8 + 6 files changed, 158 insertions(+), 67 deletions(-) diff --git a/gtk/gtkcheckmenuitem.c b/gtk/gtkcheckmenuitem.c index 4ed3f5c781..e0d947b219 100644 --- a/gtk/gtkcheckmenuitem.c +++ b/gtk/gtkcheckmenuitem.c @@ -24,6 +24,7 @@ #include "config.h" #include "gtkcheckmenuitem.h" +#include "gtkcsscustomgadgetprivate.h" #include "gtkmenuitemprivate.h" #include "gtkaccellabel.h" #include "deprecated/gtkactivatable.h" @@ -67,6 +68,7 @@ struct _GtkCheckMenuItemPrivate { GtkCssNode *indicator_node; + GtkCssGadget *indicator_gadget; guint active : 1; guint draw_as_radio : 1; @@ -123,6 +125,118 @@ G_DEFINE_TYPE_WITH_CODE (GtkCheckMenuItem, gtk_check_menu_item, GTK_TYPE_MENU_IT gtk_check_menu_item_activatable_interface_init)) G_GNUC_END_IGNORE_DEPRECATIONS; +static gboolean +gtk_check_menu_item_render_indicator (GtkCssGadget *gadget, + cairo_t *cr, + int x, + int y, + int width, + int height, + gpointer data) +{ + GtkWidget *widget = gtk_css_gadget_get_owner (gadget); + GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (widget); + GtkCheckMenuItemPrivate *priv = check_menu_item->priv; + GtkStyleContext *context; + + if (!gtk_widget_is_drawable (widget)) + return FALSE; + + context = gtk_widget_get_style_context (widget); + gtk_style_context_save_to_node (context, priv->indicator_node); + + if (priv->draw_as_radio) + gtk_render_option (context, cr, x, y, + width, height); + else + gtk_render_check (context, cr, x, y, + width, height); + + gtk_style_context_restore (context); + + return FALSE; +} + +static void +gtk_check_menu_item_measure_indicator (GtkCssGadget *gadget, + GtkOrientation orientation, + int size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline, + gpointer data) +{ + GtkWidget *widget = gtk_css_gadget_get_owner (gadget); + guint indicator_size; + + gtk_widget_style_get (widget, + "indicator-size", &indicator_size, + NULL); + + *minimum = *natural = indicator_size; +} + +static void +gtk_check_menu_item_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkAllocation clip, widget_clip; + GtkAllocation content_alloc, indicator_alloc; + GtkCssGadget *menu_item_gadget; + GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (widget); + GtkCheckMenuItemPrivate *priv = check_menu_item->priv; + gint content_baseline, toggle_size; + + GTK_WIDGET_CLASS (gtk_check_menu_item_parent_class)->size_allocate + (widget, allocation); + + menu_item_gadget = _gtk_menu_item_get_gadget (GTK_MENU_ITEM (widget)); + gtk_css_gadget_get_content_allocation (menu_item_gadget, + &content_alloc, &content_baseline); + + gtk_css_gadget_get_preferred_size (priv->indicator_gadget, + GTK_ORIENTATION_HORIZONTAL, + -1, + &indicator_alloc.width, NULL, + NULL, NULL); + gtk_css_gadget_get_preferred_size (priv->indicator_gadget, + GTK_ORIENTATION_VERTICAL, + -1, + &indicator_alloc.height, NULL, + NULL, NULL); + toggle_size = GTK_MENU_ITEM (check_menu_item)->priv->toggle_size; + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) + indicator_alloc.x = content_alloc.x + + (toggle_size - indicator_alloc.width) / 2; + else + indicator_alloc.x = content_alloc.x + content_alloc.width - toggle_size + + (toggle_size - indicator_alloc.width) / 2; + + indicator_alloc.y = content_alloc.y + + (content_alloc.height - indicator_alloc.height) / 2; + + gtk_css_gadget_allocate (check_menu_item->priv->indicator_gadget, + &indicator_alloc, + content_baseline, + &clip); + + gtk_widget_get_clip (widget, &widget_clip); + gdk_rectangle_union (&widget_clip, &clip, &widget_clip); + gtk_widget_set_clip (widget, &widget_clip); +} + +static void +gtk_check_menu_item_finalize (GObject *object) +{ + GtkCheckMenuItemPrivate *priv = GTK_CHECK_MENU_ITEM (object)->priv; + + g_clear_object (&priv->indicator_gadget); + + G_OBJECT_CLASS (gtk_check_menu_item_parent_class)->finalize (object); +} + static void gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass) { @@ -136,7 +250,9 @@ gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass) gobject_class->set_property = gtk_check_menu_item_set_property; gobject_class->get_property = gtk_check_menu_item_get_property; + gobject_class->finalize = gtk_check_menu_item_finalize; + widget_class->size_allocate = gtk_check_menu_item_size_allocate; widget_class->state_flags_changed = gtk_check_menu_item_state_flags_changed; widget_class->direction_changed = gtk_check_menu_item_direction_changed; @@ -378,17 +494,16 @@ static void gtk_check_menu_item_toggle_size_request (GtkMenuItem *menu_item, gint *requisition) { - guint toggle_spacing; - guint indicator_size; - + GtkCheckMenuItem *check_menu_item; + g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (menu_item)); - - gtk_widget_style_get (GTK_WIDGET (menu_item), - "toggle-spacing", &toggle_spacing, - "indicator-size", &indicator_size, - NULL); - *requisition = indicator_size + toggle_spacing; + check_menu_item = GTK_CHECK_MENU_ITEM (menu_item); + gtk_css_gadget_get_preferred_size (check_menu_item->priv->indicator_gadget, + GTK_ORIENTATION_HORIZONTAL, + -1, + requisition, NULL, + NULL, NULL); } /** @@ -551,6 +666,14 @@ gtk_check_menu_item_init (GtkCheckMenuItem *check_menu_item) gtk_css_node_set_state (priv->indicator_node, gtk_css_node_get_state (widget_node)); g_signal_connect_object (priv->indicator_node, "style-changed", G_CALLBACK (node_style_changed_cb), check_menu_item, 0); g_object_unref (priv->indicator_node); + + priv->indicator_gadget = + gtk_css_custom_gadget_new_for_node (priv->indicator_node, + GTK_WIDGET (check_menu_item), + gtk_check_menu_item_measure_indicator, + NULL, + gtk_check_menu_item_render_indicator, + NULL, NULL); } static gint @@ -635,66 +758,9 @@ static void gtk_real_check_menu_item_draw_indicator (GtkCheckMenuItem *check_menu_item, cairo_t *cr) { - GtkCheckMenuItemPrivate *priv = check_menu_item->priv; - GtkWidget *widget; - GtkAllocation allocation; - GtkStyleContext *context; - guint border_width; - guint offset; - guint toggle_size; - guint toggle_spacing; - guint indicator_size; - gint x, y; - GtkStateFlags state; - GtkBorder padding; - - widget = GTK_WIDGET (check_menu_item); - - if (!gtk_widget_is_drawable (widget)) - return; - - context = gtk_widget_get_style_context (widget); - state = gtk_widget_get_state_flags (widget); - gtk_style_context_get_padding (context, state, &padding); - - gtk_widget_get_allocation (widget, &allocation); - - gtk_widget_style_get (widget, - "toggle-spacing", &toggle_spacing, - "indicator-size", &indicator_size, - NULL); - - toggle_size = GTK_MENU_ITEM (check_menu_item)->priv->toggle_size; - border_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); - offset = border_width + padding.left + 2; - - if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) - { - x = offset + - (toggle_size - toggle_spacing - indicator_size) / 2; - } - else - { - x = allocation.width - - offset - toggle_size + toggle_spacing + - (toggle_size - toggle_spacing - indicator_size) / 2; - } - - y = (allocation.height - indicator_size) / 2; - - gtk_style_context_save_to_node (context, priv->indicator_node); - - if (priv->draw_as_radio) - gtk_render_option (context, cr, x, y, - indicator_size, indicator_size); - else - gtk_render_check (context, cr, x, y, - indicator_size, indicator_size); - - gtk_style_context_restore (context); + gtk_css_gadget_draw (check_menu_item->priv->indicator_gadget, cr); } - static void gtk_check_menu_item_get_property (GObject *object, guint prop_id, diff --git a/gtk/gtkmenuitem.c b/gtk/gtkmenuitem.c index a7310f0957..ba852517ea 100644 --- a/gtk/gtkmenuitem.c +++ b/gtk/gtkmenuitem.c @@ -1035,6 +1035,12 @@ gtk_menu_item_init (GtkMenuItem *menu_item) NULL, NULL); } +GtkCssGadget * +_gtk_menu_item_get_gadget (GtkMenuItem *menu_item) +{ + return menu_item->priv->gadget; +} + /** * gtk_menu_item_new: * diff --git a/gtk/gtkmenuitemprivate.h b/gtk/gtkmenuitemprivate.h index 5f3a6da65f..0832951e7e 100644 --- a/gtk/gtkmenuitemprivate.h +++ b/gtk/gtkmenuitemprivate.h @@ -54,6 +54,7 @@ struct _GtkMenuItemPrivate guint reserve_indicator : 1; }; +GtkCssGadget * _gtk_menu_item_get_gadget (GtkMenuItem *menu_item); void _gtk_menu_item_refresh_accel_path (GtkMenuItem *menu_item, const gchar *prefix, GtkAccelGroup *accel_group, diff --git a/gtk/theme/Adwaita/_common.scss b/gtk/theme/Adwaita/_common.scss index 54b94f343c..6f407f2deb 100644 --- a/gtk/theme/Adwaita/_common.scss +++ b/gtk/theme/Adwaita/_common.scss @@ -2096,6 +2096,8 @@ switch { color: mix($fg_color, $bg_color, 70%); -gtk-icon-source: -gtk-icontheme('#{$a}-symbolic'); -gtk-icon-shadow: none; + &:dir(ltr) { margin-right: 7px; } + &:dir(rtl) { margin-left: 7px; } &:hover, &:insensitive { -gtk-icon-source: -gtk-icontheme('#{$a}-symbolic'); -gtk-icon-shadow: none; diff --git a/gtk/theme/Adwaita/gtk-contained-dark.css b/gtk/theme/Adwaita/gtk-contained-dark.css index 887d293a5f..a48cfdac99 100644 --- a/gtk/theme/Adwaita/gtk-contained-dark.css +++ b/gtk/theme/Adwaita/gtk-contained-dark.css @@ -2771,6 +2771,10 @@ menu menuitem check { color: #b8bab8; -gtk-icon-source: -gtk-icontheme("checkbox-symbolic"); -gtk-icon-shadow: none; } + menu menuitem check:dir(ltr) { + margin-right: 7px; } + menu menuitem check:dir(rtl) { + margin-left: 7px; } menu menuitem check:hover, menu menuitem check:insensitive { -gtk-icon-source: -gtk-icontheme("checkbox-symbolic"); -gtk-icon-shadow: none; } @@ -2933,6 +2937,10 @@ menu menuitem radio { color: #b8bab8; -gtk-icon-source: -gtk-icontheme("radio-symbolic"); -gtk-icon-shadow: none; } + menu menuitem radio:dir(ltr) { + margin-right: 7px; } + menu menuitem radio:dir(rtl) { + margin-left: 7px; } menu menuitem radio:hover, menu menuitem radio:insensitive { -gtk-icon-source: -gtk-icontheme("radio-symbolic"); -gtk-icon-shadow: none; } diff --git a/gtk/theme/Adwaita/gtk-contained.css b/gtk/theme/Adwaita/gtk-contained.css index 6bbefba644..a328289a4d 100644 --- a/gtk/theme/Adwaita/gtk-contained.css +++ b/gtk/theme/Adwaita/gtk-contained.css @@ -2861,6 +2861,10 @@ menu menuitem check { color: #666a6b; -gtk-icon-source: -gtk-icontheme("checkbox-symbolic"); -gtk-icon-shadow: none; } + menu menuitem check:dir(ltr) { + margin-right: 7px; } + menu menuitem check:dir(rtl) { + margin-left: 7px; } menu menuitem check:hover, menu menuitem check:insensitive { -gtk-icon-source: -gtk-icontheme("checkbox-symbolic"); -gtk-icon-shadow: none; } @@ -3095,6 +3099,10 @@ menu menuitem radio { color: #666a6b; -gtk-icon-source: -gtk-icontheme("radio-symbolic"); -gtk-icon-shadow: none; } + menu menuitem radio:dir(ltr) { + margin-right: 7px; } + menu menuitem radio:dir(rtl) { + margin-left: 7px; } menu menuitem radio:hover, menu menuitem radio:insensitive { -gtk-icon-source: -gtk-icontheme("radio-symbolic"); -gtk-icon-shadow: none; } -- 2.30.2